package chooongg.base.utils

import android.text.format.DateUtils.isToday
import androidx.annotation.IntDef
import chooongg.base.UNDEFINED_INT
import java.text.DateFormat
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.min

/**
 * 时间常量
 */
object TimeConstants {

    const val MSEC = 1
    const val SEC = 1000
    const val MIN = 60000
    const val HOUR = 3600000
    const val DAY = 86400000

    @IntDef(MSEC, SEC, MIN, HOUR, DAY)
    annotation class Unit
}

private val SDF_THREAD_LOCAL = ThreadLocal<SimpleDateFormat>()

private fun getDefaultFormat(): SimpleDateFormat {
    var simpleDateFormat = SDF_THREAD_LOCAL.get()
    if (simpleDateFormat == null) {
        simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
        SDF_THREAD_LOCAL.set(simpleDateFormat)
    }
    return simpleDateFormat
}

/**
 * 时间戳转字符串
 *
 * @param millis The milliseconds.
 * @param format The format.
 */
@JvmOverloads
fun millis2String(millis: Long, format: DateFormat = getDefaultFormat()): String {
    return format.format(Date(millis))
}

/**
 * 字符串转时间戳
 *
 * @param time   The formatted time string.
 * @param format The format.
 */
@JvmOverloads
fun string2Millis(time: String, format: DateFormat = getDefaultFormat()): Long {
    try {
        return format.parse(time)!!.time
    } catch (e: ParseException) {
        e.printStackTrace()
    }

    return -1
}

/**
 * 字符串转Date
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return the date
 */
@JvmOverloads
fun string2Date(time: String, format: DateFormat = getDefaultFormat()): Date? {
    try {
        return format.parse(time)
    } catch (e: ParseException) {
        e.printStackTrace()
    }
    return null
}

/**
 * Date转字符串
 *
 * @param date   The date.
 * @param format The format.
 * @return the formatted time string
 */
@JvmOverloads
fun date2String(date: Date, format: DateFormat = getDefaultFormat()): String {
    return format.format(date)
}

/**
 * 获取两个时间差（单位：unit）
 *
 * @param time1  The first formatted time string.
 * @param time2  The second formatted time string.
 * @param format The format.
 * @param unit   The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the time span, in unit
 */
@JvmOverloads
fun getTimeSpan(
    time1: String,
    time2: String,
    format: DateFormat = getDefaultFormat(),
    @TimeConstants.Unit unit: Int
): Long {
    return millis2TimeSpan(string2Millis(time1, format) - string2Millis(time2, format), unit)
}

/**
 * 获取两个时间差（单位：unit）
 *
 * @param date1 The first date.
 * @param date2 The second date.
 * @param unit  The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the time span, in unit
 */
fun getTimeSpan(date1: Date, date2: Date, @TimeConstants.Unit unit: Int): Long {
    return millis2TimeSpan(date1.time - date2.time, unit)
}

/**
 * 获取两个时间差（单位：unit）
 *
 * @param millis1 The first milliseconds.
 * @param millis2 The second milliseconds.
 * @param unit    The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the time span, in unit
 */
fun getTimeSpan(millis1: Long, millis2: Long, @TimeConstants.Unit unit: Int): Long {
    return millis2TimeSpan(millis1 - millis2, unit)
}

/**
 * 获取合适型两个时间差
 *
 * @param time1     The first formatted time string.
 * @param time2     The second formatted time string.
 * @param format    The format.
 * @param precision The precision of time span.
 *
 *  * precision = 0, return null
 *  * precision = 1, return 天
 *  * precision = 2, return 天, 小时
 *  * precision = 3, return 天, 小时, 分钟
 *  * precision = 4, return 天, 小时, 分钟, 秒
 *  * precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒
 *
 * @return the fit time span
 */
@JvmOverloads
fun getFitTimeSpan(
    time1: String,
    time2: String,
    format: DateFormat = getDefaultFormat(),
    precision: Int
): String? {
    val delta = string2Millis(time1, format) - string2Millis(time2, format)
    return millis2FitTimeSpan(delta, precision)
}

/**
 * 获取合适型两个时间差
 *
 * @param date1     The first date.
 * @param date2     The second date.
 * @param precision The precision of time span.
 *
 *  * precision = 0, return null
 *  * precision = 1, return 天
 *  * precision = 2, return 天, 小时
 *  * precision = 3, return 天, 小时, 分钟
 *  * precision = 4, return 天, 小时, 分钟, 秒
 *  * precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒
 *
 * @return the fit time span
 */
fun getFitTimeSpan(date1: Date, date2: Date, precision: Int): String? {
    return millis2FitTimeSpan(date1.time - date2.time, precision)
}

/**
 * 获取合适型两个时间差
 *
 * @param millis1   The first milliseconds.
 * @param millis2   The second milliseconds.
 * @param precision The precision of time span.
 *
 *  * precision = 0, return null
 *  * precision = 1, return 天
 *  * precision = 2, return 天, 小时
 *  * precision = 3, return 天, 小时, 分钟
 *  * precision = 4, return 天, 小时, 分钟, 秒
 *  * precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒
 *
 * @return the fit time span
 */
fun getFitTimeSpan(millis1: Long, millis2: Long, precision: Int): String? {
    return millis2FitTimeSpan(millis1 - millis2, precision)
}

/**
 * 获取当前时间戳
 *
 * @return the current time in milliseconds
 */
fun getNowMills(): Long {
    return System.currentTimeMillis()
}

/**
 * 获取当前时间字符串
 *
 * @param format The format.
 * @return the current formatted time string
 */
@JvmOverloads
fun getNowString(format: DateFormat = getDefaultFormat()): String {
    return millis2String(System.currentTimeMillis(), format)
}

/**
 * 获取当前Date
 *
 * @return the current date
 */
fun getNowDate(): Date {
    return Date()
}

/**
 * 获取与当前时间的差（单位：unit）
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @param unit   The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the time span by now, in unit
 */
@JvmOverloads
fun getTimeSpanByNow(
    time: String,
    format: DateFormat = getDefaultFormat(),
    @TimeConstants.Unit unit: Int
): Long {
    return getTimeSpan(time, getNowString(format), format, unit)
}

/**
 * 获取与当前时间的差（单位：unit）
 *
 * @param date The date.
 * @param unit The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the time span by now, in unit
 */
fun getTimeSpanByNow(date: Date, @TimeConstants.Unit unit: Int): Long {
    return getTimeSpan(date, Date(), unit)
}

/**
 * 获取与当前时间的差（单位：unit）
 *
 * @param millis The milliseconds.
 * @param unit   The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the time span by now, in unit
 */
fun getTimeSpanByNow(millis: Long, @TimeConstants.Unit unit: Int): Long {
    return getTimeSpan(millis, System.currentTimeMillis(), unit)
}

/**
 * 获取合适型与当前时间的差
 *
 * @param time      The formatted time string.
 * @param format    The format.
 * @param precision The precision of time span.
 *
 *  * precision = 0，返回 null
 *  * precision = 1，返回天
 *  * precision = 2，返回天和小时
 *  * precision = 3，返回天、小时和分钟
 *  * precision = 4，返回天、小时、分钟和秒
 *  * precision &gt;= 5，返回天、小时、分钟、秒和毫秒
 *
 * @return the fit time span by now
 */
@JvmOverloads
fun getFitTimeSpanByNow(
    time: String,
    format: DateFormat = getDefaultFormat(),
    precision: Int
): String? {
    return getFitTimeSpan(time, getNowString(format), format, precision)
}

/**
 * 获取合适型与当前时间的差
 *
 * @param date      The date.
 * @param precision The precision of time span.
 *
 *  * precision = 0，返回 null
 *  * precision = 1，返回天
 *  * precision = 2，返回天和小时
 *  * precision = 3，返回天、小时和分钟
 *  * precision = 4，返回天、小时、分钟和秒
 *  * precision &gt;= 5，返回天、小时、分钟、秒和毫秒
 *
 * @return the fit time span by now
 */
fun getFitTimeSpanByNow(date: Date, precision: Int): String? {
    return getFitTimeSpan(date, getNowDate(), precision)
}

/**
 * 获取合适型与当前时间的差
 *
 * @param millis    The milliseconds.
 * @param precision The precision of time span.
 *
 *  * precision = 0，返回 null
 *  * precision = 1，返回天
 *  * precision = 2，返回天和小时
 *  * precision = 3，返回天、小时和分钟
 *  * precision = 4，返回天、小时、分钟和秒
 *  * precision &gt;= 5，返回天、小时、分钟、秒和毫秒
 *
 * @return the fit time span by now
 */
fun getFitTimeSpanByNow(millis: Long, precision: Int): String? {
    return getFitTimeSpan(millis, System.currentTimeMillis(), precision)
}

/**
 * 获取友好型与当前时间的差
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return the friendly time span by now
 *
 *  * 如果小于 1 秒钟内，显示刚刚
 *  * 如果在 1 分钟内，显示 XXX秒前
 *  * 如果在 1 小时内，显示 XXX分钟前
 *  * 如果在 1 小时外的今天内，显示今天15:32
 *  * 如果是昨天的，显示昨天15:32
 *  * 其余显示，2016-10-15
 *  * 时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007
 *
 */
@JvmOverloads
fun getFriendlyTimeSpanByNow(time: String, format: DateFormat = getDefaultFormat()): String {
    return getFriendlyTimeSpanByNow(string2Millis(time, format))
}

/**
 * 获取友好型与当前时间的差
 *
 * @param date The date.
 * @return the friendly time span by now
 *
 *  * 如果小于 1 秒钟内，显示刚刚
 *  * 如果在 1 分钟内，显示 XXX秒前
 *  * 如果在 1 小时内，显示 XXX分钟前
 *  * 如果在 1 小时外的今天内，显示今天15:32
 *  * 如果是昨天的，显示昨天15:32
 *  * 其余显示，2016-10-15
 *  * 时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007
 *
 */
fun getFriendlyTimeSpanByNow(date: Date): String {
    return getFriendlyTimeSpanByNow(date.time)
}

/**
 * 获取友好型与当前时间的差
 *
 * @param millis The milliseconds.
 * @return the friendly time span by now
 *
 *  * 如果小于 1 秒钟内，显示刚刚
 *  * 如果在 1 分钟内，显示 XXX秒前
 *  * 如果在 1 小时内，显示 XXX分钟前
 *  * 如果在 1 小时外的今天内，显示今天15:32
 *  * 如果是昨天的，显示昨天15:32
 *  * 其余显示，2016-10-15
 *  * 时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007
 *
 */
fun getFriendlyTimeSpanByNow(millis: Long): String {
    if (millis == -1L) return "未知"
    val now = System.currentTimeMillis()
    val span = now - millis
    if (span < 0) return String.format("%tc", millis)
    return when {
        span < 1000 -> "刚刚"
        span < TimeConstants.MIN -> String.format(
            Locale.getDefault(),
            "%d秒前",
            span / TimeConstants.SEC
        )
        span < TimeConstants.HOUR -> String.format(
            Locale.getDefault(),
            "%d分钟前",
            span / TimeConstants.MIN
        )
        // 获取当天 00:00
        else -> {
            val wee = getWeeOfToday()
            when {
                millis >= wee -> String.format("今天%tR", millis)
                millis >= wee - TimeConstants.DAY -> String.format("昨天%tR", millis)
                else -> String.format("%tF", millis)
            }
        }
    }
}

fun getWeeOfToday(): Long {
    val cal = Calendar.getInstance()
    cal.set(Calendar.HOUR_OF_DAY, 0)
    cal.set(Calendar.SECOND, 0)
    cal.set(Calendar.MINUTE, 0)
    cal.set(Calendar.MILLISECOND, 0)
    return cal.timeInMillis
}

/**
 * 获取与给定时间等于时间差的时间戳
 *
 * @param millis   The milliseconds.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the milliseconds differ time span
 */
fun getMillis(millis: Long, timeSpan: Long, @TimeConstants.Unit unit: Int): Long {
    return millis + timeSpan2Millis(timeSpan, unit)
}

/**
 * 获取与给定时间等于时间差的时间戳
 *
 * @param time     The formatted time string.
 * @param format   The format.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the milliseconds differ time span.
 */
@JvmOverloads
fun getMillis(
    time: String,
    format: DateFormat = getDefaultFormat(),
    timeSpan: Long,
    @TimeConstants.Unit unit: Int
): Long {
    return string2Millis(time, format) + timeSpan2Millis(timeSpan, unit)
}

/**
 * 获取与给定时间等于时间差的时间戳
 *
 * @param date     The date.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the milliseconds differ time span.
 */
fun getMillis(date: Date, timeSpan: Long, @TimeConstants.Unit unit: Int): Long {
    return date.time + timeSpan2Millis(timeSpan, unit)
}

/**
 * 获取与给定时间等于时间差的时间字符串
 *
 * @param millis   The milliseconds.
 * @param format   The format.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the formatted time string differ time span
 */
@JvmOverloads
fun getString(
    millis: Long,
    format: DateFormat = getDefaultFormat(),
    timeSpan: Long,
    @TimeConstants.Unit unit: Int
): String {
    return millis2String(millis + timeSpan2Millis(timeSpan, unit), format)
}

/**
 * 获取与给定时间等于时间差的时间字符串
 *
 * @param time     The formatted time string.
 * @param format   The format.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the formatted time string differ time span
 */
@JvmOverloads
fun getString(
    time: String,
    format: DateFormat = getDefaultFormat(),
    timeSpan: Long,
    @TimeConstants.Unit unit: Int
): String {
    return millis2String(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit), format)
}

/**
 * 获取与给定时间等于时间差的时间字符串
 *
 * @param date     The date.
 * @param format   The format.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the formatted time string differ time span
 */
@JvmOverloads
fun getString(
    date: Date,
    format: DateFormat = getDefaultFormat(),
    timeSpan: Long,
    @TimeConstants.Unit unit: Int
): String {
    return millis2String(date.time + timeSpan2Millis(timeSpan, unit), format)
}

/**
 * 获取与给定时间等于时间差的Date
 *
 * @param millis   The milliseconds.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the date differ time span
 */
fun getDate(millis: Long, timeSpan: Long, @TimeConstants.Unit unit: Int): Date {
    return Date(millis + timeSpan2Millis(timeSpan, unit))
}

/**
 * 获取与给定时间等于时间差的Date
 *
 * @param time     The formatted time string.
 * @param format   The format.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the date differ time span
 */
@JvmOverloads
fun getDate(
    time: String,
    format: DateFormat = getDefaultFormat(),
    timeSpan: Long,
    @TimeConstants.Unit unit: Int
): Date {
    return Date(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit))
}

/**
 * 获取与给定时间等于时间差的Date
 *
 * @param date     The date.
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the date differ time span
 */
fun getDate(date: Date, timeSpan: Long, @TimeConstants.Unit unit: Int): Date {
    return Date(date.time + timeSpan2Millis(timeSpan, unit))
}

/**
 * 获取与当前时间等于时间差的时间戳
 *
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the milliseconds differ time span by now
 */
fun getMillisByNow(timeSpan: Long, @TimeConstants.Unit unit: Int): Long {
    return getMillis(getNowMills(), timeSpan, unit)
}

/**
 * 获取与当前时间等于时间差的时间字符串
 *
 * @param timeSpan The time span.
 * @param format   The format.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the formatted time string differ time span by now
 */
@JvmOverloads
fun getStringByNow(
    timeSpan: Long,
    format: DateFormat = getDefaultFormat(),
    @TimeConstants.Unit unit: Int
): String {
    return getString(getNowMills(), format, timeSpan, unit)
}

/**
 * 获取与当前时间等于时间差的 Date
 *
 * @param timeSpan The time span.
 * @param unit     The unit of time span.
 *
 *  * [TimeConstants.MSEC]
 *  * [TimeConstants.SEC]
 *  * [TimeConstants.MIN]
 *  * [TimeConstants.HOUR]
 *  * [TimeConstants.DAY]
 *
 * @return the date differ time span by now
 */
fun getDateByNow(timeSpan: Long, @TimeConstants.Unit unit: Int): Date {
    return getDate(getNowMills(), timeSpan, unit)
}

/**
 * 判断是否今天
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return `true`: yes<br></br>`false`: no
 */
@JvmOverloads
fun isToday(time: String, format: DateFormat = getDefaultFormat()): Boolean {
    return isToday(string2Millis(time, format))
}

/**
 * 判断是否今天
 *
 * @param date The date.
 * @return `true`: yes<br></br>`false`: no
 */
fun isToday(date: Date): Boolean {
    return isToday(date.time)
}

/**
 * 判断是否今天
 *
 * @param millis The milliseconds.
 * @return `true`: yes<br></br>`false`: no
 */
fun isToday(millis: Long): Boolean {
    val wee = getWeeOfToday()
    return millis >= wee && millis < wee + TimeConstants.DAY
}

/**
 * 判断是否闰年
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return `true`: yes<br></br>`false`: no
 */
@JvmOverloads
fun isLeapYear(time: String, format: DateFormat = getDefaultFormat()): Boolean {
    return isLeapYear(string2Date(time, format) ?: return false)
}

/**
 * 判断是否闰年
 *
 * @param date The date.
 * @return `true`: yes<br></br>`false`: no
 */
fun isLeapYear(date: Date): Boolean {
    val cal = Calendar.getInstance()
    cal.time = date
    val year = cal.get(Calendar.YEAR)
    return isLeapYear(year)
}

/**
 * 判断是否闰年
 *
 * @param millis The milliseconds.
 * @return `true`: yes<br></br>`false`: no
 */
fun isLeapYear(millis: Long): Boolean {
    return isLeapYear(Date(millis))
}

/**
 * 判断是否闰年
 *
 * @param year The year.
 * @return `true`: yes<br></br>`false`: no
 */
fun isLeapYear(year: Int): Boolean {
    return year % 4 == 0 && year % 100 != 0 || year % 400 == 0
}

/**
 * 获取中式星期
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return the day of week in Chinese
 */
@JvmOverloads
fun getChineseWeek(time: String, format: DateFormat = getDefaultFormat()): String? {
    return getChineseWeek(string2Date(time, format) ?: return null)
}

/**
 * 获取中式星期
 *
 * @param date The date.
 * @return the day of week in Chinese
 */
fun getChineseWeek(date: Date): String {
    return SimpleDateFormat("E", Locale.CHINA).format(date)
}

/**
 * 获取中式星期
 *
 * @param millis The milliseconds.
 * @return the day of week in Chinese
 */
fun getChineseWeek(millis: Long): String {
    return getChineseWeek(Date(millis))
}

/**
 * 获取美式式星期
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return the day of week in US
 */
@JvmOverloads
fun getUSWeek(time: String, format: DateFormat = getDefaultFormat()): String? {
    return getUSWeek(string2Date(time, format) ?: return null)
}

/**
 * 获取美式式星期
 *
 * @param date The date.
 * @return the day of week in US
 */
fun getUSWeek(date: Date): String {
    return SimpleDateFormat("EEEE", Locale.US).format(date)
}

/**
 * 获取美式式星期
 *
 * @param millis The milliseconds.
 * @return the day of week in US
 */
fun getUSWeek(millis: Long): String {
    return getUSWeek(Date(millis))
}

/**
 * 根据日历字段获取值
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @param field  The given calendar field.
 *
 *  * [Calendar.ERA]
 *  * [Calendar.YEAR]
 *  * [Calendar.MONTH]
 *  * ...
 *  * [Calendar.DST_OFFSET]
 *
 * @return the value of the given calendar field
 */
@JvmOverloads
fun getValueByCalendarField(
    time: String,
    format: DateFormat = getDefaultFormat(),
    field: Int
): Int {
    return getValueByCalendarField(string2Date(time, format) ?: return UNDEFINED_INT, field)
}

/**
 * 根据日历字段获取值
 *
 * @param date  The date.
 * @param field The given calendar field.
 *
 *  * [Calendar.ERA]
 *  * [Calendar.YEAR]
 *  * [Calendar.MONTH]
 *  * ...
 *  * [Calendar.DST_OFFSET]
 *
 * @return the value of the given calendar field
 */
fun getValueByCalendarField(date: Date, field: Int): Int {
    val cal = Calendar.getInstance()
    cal.time = date
    return cal.get(field)
}

/**
 * 根据日历字段获取值
 *
 * @param millis The milliseconds.
 * @param field  The given calendar field.
 *
 *  * [Calendar.ERA]
 *  * [Calendar.YEAR]
 *  * [Calendar.MONTH]
 *  * ...
 *  * [Calendar.DST_OFFSET]
 *
 * @return the value of the given calendar field
 */
fun getValueByCalendarField(millis: Long, field: Int): Int {
    val cal = Calendar.getInstance()
    cal.timeInMillis = millis
    return cal.get(field)
}

private val CHINESE_ZODIAC = arrayOf("猴", "鸡", "狗", "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊")

/**
 * 获取生肖
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return the Chinese zodiac
 */
@JvmOverloads
fun getChineseZodiac(time: String, format: DateFormat = getDefaultFormat()): String? {
    return getChineseZodiac(string2Date(time, format) ?: return null)
}

/**
 * 获取生肖
 *
 * @param date The date.
 * @return the Chinese zodiac
 */
fun getChineseZodiac(date: Date): String {
    val cal = Calendar.getInstance()
    cal.time = date
    return CHINESE_ZODIAC[cal.get(Calendar.YEAR) % 12]
}

/**
 * 获取生肖
 *
 * @param millis The milliseconds.
 * @return the Chinese zodiac
 */
fun getChineseZodiac(millis: Long): String {
    return getChineseZodiac(Date(millis))
}

/**
 * 获取生肖
 *
 * @param year The year.
 * @return the Chinese zodiac
 */
fun getChineseZodiac(year: Int): String {
    return CHINESE_ZODIAC[year % 12]
}

private val ZODIAC_FLAGS = intArrayOf(20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22)
private val ZODIAC =
    arrayOf("水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "魔羯座")

/**
 * 获取星座
 *
 * @param time   The formatted time string.
 * @param format The format.
 * @return the zodiac
 */
@JvmOverloads
fun getZodiac(time: String, format: DateFormat = getDefaultFormat()): String? {
    return getZodiac(string2Date(time, format) ?: return null)
}

/**
 * 获取星座
 *
 * @param date The date.
 * @return the zodiac
 */
fun getZodiac(date: Date): String {
    val cal = Calendar.getInstance()
    cal.time = date
    val month = cal.get(Calendar.MONTH) + 1
    val day = cal.get(Calendar.DAY_OF_MONTH)
    return getZodiac(month, day)
}

/**
 * 获取星座
 *
 * @param millis The milliseconds.
 * @return the zodiac
 */
fun getZodiac(millis: Long): String {
    return getZodiac(Date(millis))
}

/**
 * 获取星座
 *
 * @param month The month.
 * @param day   The day.
 * @return the zodiac
 */
fun getZodiac(month: Int, day: Int): String {
    return ZODIAC[if (day >= ZODIAC_FLAGS[month - 1])
        month - 1
    else
        (month + 10) % 12]
}

private fun timeSpan2Millis(timeSpan: Long, @TimeConstants.Unit unit: Int): Long {
    return timeSpan * unit
}

private fun millis2TimeSpan(millis: Long, @TimeConstants.Unit unit: Int): Long {
    return millis / unit
}

private fun millis2FitTimeSpan(millis: Long, precision: Int): String? {
    var millis1 = millis
    var precision1 = precision
    if (precision1 <= 0) return null
    precision1 = min(precision1, 5)
    val units = arrayOf("天", "小时", "分钟", "秒", "毫秒")
    if (millis1 == 0L) return 0.toString() + units[precision1 - 1]
    val sb = StringBuilder()
    if (millis1 < 0) {
        sb.append("-")
        millis1 = -millis1
    }
    val unitLen = intArrayOf(86400000, 3600000, 60000, 1000, 1)
    for (i in 0 until precision1) {
        if (millis1 >= unitLen[i]) {
            val mode = millis1 / unitLen[i]
            millis1 -= mode * unitLen[i]
            sb.append(mode).append(units[i])
        }
    }
    return sb.toString()
}